package com.house365.core.http;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import javax.net.ssl.SSLHandshakeException;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.AbstractHttpMessage;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import com.house365.core.constant.CorePreferences;
import com.house365.core.http.ex.SSLSocketFactoryEx;

/**
 * @author py
 * @date 2012-6-28
 * @comment
 */
public abstract class BaseHttpAPI {
	private final DefaultHttpClient client;
	// private final HttpURLConnectionUtil standerHttp;
	public static final int CHANNLE_TYPE_STANDER = 1;
	public static final int CHANNLE_TYPE_COMMONS = 2;
	protected int TIMEOUT = 30000;// 
	protected String CHARENCODE = HTTP.UTF_8;
	protected boolean USEGZIP = true;
	protected int HTTP_PORT = 80;
	protected int HTTPS_PORT = 443;
	protected int BUFFER_SIZE = 8192;

	/**
	 * http请求默认参数，有子类实现
	 * @return
	 */
	public abstract int setConnectTimeOut();

	public abstract String setCharEncode();

	public abstract boolean setUSEGIZP();

	public abstract int setHttpPort();

	public abstract int setHttpsPort();

	public BaseHttpAPI() {
		 
		TIMEOUT = setConnectTimeOut();
		CHARENCODE = setCharEncode();
		USEGZIP = setUSEGIZP();
		HTTP_PORT = setHttpPort();
		HTTPS_PORT = setHttpsPort();
		client = createDefHttpClient();
		//client.setHttpRequestRetryHandler(requestRetryHandler);

	}
	public DefaultHttpClient createDefHttpClient() {
		try {
			// Sets up the http part of the service.
			
			// Register the "http" protocol scheme, it is required
			// by the default operator to look up socket factories.
			  KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			   trustStore.load(null, null);
 
			   SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
			   sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			   
			   
			   
			//final SocketFactory sf = ;
			   final SchemeRegistry supportedSchemes = new SchemeRegistry();

			supportedSchemes.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), HTTP_PORT));
			supportedSchemes.register(new Scheme("https", sf, HTTPS_PORT));

			// Set some client http client parameter defaults.
			final HttpParams httpParams = createHttpParams();

			final ClientConnectionManager ccm = new ThreadSafeClientConnManager(
					httpParams, supportedSchemes);
			return new DefaultHttpClient(ccm, httpParams);
		} catch (Exception e) {
			e.printStackTrace();
			return createDefClient();
		} 
	}
	public DefaultHttpClient createDefClient() {
			// Sets up the http part of the service.
			final SchemeRegistry supportedSchemes = new SchemeRegistry();

			// Register the "http" protocol scheme, it is required
			// by the default operator to look up socket factories.
			   
			   
			final SocketFactory sf = PlainSocketFactory.getSocketFactory();
			supportedSchemes.register(new Scheme("http", sf, HTTP_PORT));
			supportedSchemes.register(new Scheme("https", SSLSocketFactory
					.getSocketFactory(), HTTPS_PORT));

			// Set some client http client parameter defaults.
			final HttpParams httpParams = createHttpParams();

			final ClientConnectionManager ccm = new ThreadSafeClientConnManager(
					httpParams, supportedSchemes);
			return new DefaultHttpClient(ccm, httpParams);
	}
	public DefaultHttpClient createDefClient(HttpParams httpParams) {
		// Sets up the http part of the service.
		final SchemeRegistry supportedSchemes = new SchemeRegistry();

		// Register the "http" protocol scheme, it is required
		// by the default operator to look up socket factories.
		   
		   
		final SocketFactory sf = PlainSocketFactory.getSocketFactory();
		supportedSchemes.register(new Scheme("http", sf, HTTP_PORT));
		supportedSchemes.register(new Scheme("https", SSLSocketFactory
				.getSocketFactory(), HTTPS_PORT));

		// Set some client http client parameter defaults.

		final ClientConnectionManager ccm = new ThreadSafeClientConnManager(
				httpParams, supportedSchemes);
		return new DefaultHttpClient(ccm, httpParams);
}
	
	/**
	 * Create the default HTTP protocol parameters.
	 */
	private final HttpParams createHttpParams() {
		final HttpParams params = new BasicHttpParams();

		// Turn off stale checking. Our connections break all the time anyway,
		// and it's not worth it to pay the penalty of checking every time.
		HttpConnectionParams.setStaleCheckingEnabled(params, false);
		HttpConnectionParams.setConnectionTimeout(params, TIMEOUT);
		HttpConnectionParams.setSoTimeout(params, TIMEOUT);
		HttpConnectionParams.setSocketBufferSize(params, BUFFER_SIZE);
		
		 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		 HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
		// HttpClientParams.setRedirecting(params, false);

		return params;
	}
	public DefaultHttpClient createHttpClientWithHttpParams(HttpParams httpParams) {
		try {
			// Sets up the http part of the service.
			
			// Register the "http" protocol scheme, it is required
			// by the default operator to look up socket factories.
			  KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			   trustStore.load(null, null);
 
			   SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
			   sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			   
			   
			   
			//final SocketFactory sf = ;
			   final SchemeRegistry supportedSchemes = new SchemeRegistry();

			supportedSchemes.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), HTTP_PORT));
			supportedSchemes.register(new Scheme("https", sf, HTTPS_PORT));

			// Set some client http client parameter defaults.

			final ClientConnectionManager ccm = new ThreadSafeClientConnManager(
					httpParams, supportedSchemes);
			return new DefaultHttpClient(ccm, httpParams);
		} catch (Exception e) {
			e.printStackTrace();
			return createDefClient();
		} 
	}
	private final HttpParams createHttpParams(int timeout) {
		final HttpParams params = new BasicHttpParams();

		// Turn off stale checking. Our connections break all the time anyway,
		// and it's not worth it to pay the penalty of checking every time.
		HttpConnectionParams.setStaleCheckingEnabled(params, false);
		HttpConnectionParams.setConnectionTimeout(params, timeout);
		HttpConnectionParams.setSoTimeout(params, TIMEOUT);
		HttpConnectionParams.setSocketBufferSize(params, BUFFER_SIZE);
		
		 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
		 HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
		// HttpClientParams.setRedirecting(params, false);

		return params;
	}
	

	// �쳣�Զ��ָ�����, ʹ��HttpRequestRetryHandler�ӿ�ʵ��������쳣�ָ�
	private HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {
		// �Զ���Ļָ�����
		public boolean retryRequest(IOException exception, int executionCount,
				HttpContext context) {
			// ���ûָ����ԣ��ڷ����쳣ʱ���Զ�����3��
			if (executionCount >= 3) {
				// Do not retry if over max retry count
				return false;
			}
			if (exception instanceof NoHttpResponseException) {
				// Retry if the server dropped connection on us
				return true;
			}
			if (exception instanceof SSLHandshakeException) {
				// Do not retry on SSL handshake exception
				return false;
			}
			HttpRequest request = (HttpRequest) context
					.getAttribute(ExecutionContext.HTTP_REQUEST);
			boolean idempotent = (request instanceof HttpEntityEnclosingRequest);
			if (!idempotent) {
				// Retry if the request is considered idempotent
				return true;
			}
			return false;
		}
	};
	// ʹ��ResponseHandler�ӿڴ�����Ӧ��HttpClientʹ��ResponseHandler���Զ��������ӵ��ͷţ�����˶����ӵ��ͷŹ���
	private ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
		// �Զ�����Ӧ����
		public String handleResponse(HttpResponse response)
				throws ClientProtocolException, IOException {
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				Header header = entity.getContentEncoding();
				if (header != null) {
					String contentEncoding = header.getValue();
					if (contentEncoding != null) {
						if (contentEncoding.contains("gzip")) {
							entity = new GzipDecompressingEntity(entity);
						}
					}
				}
				String charset = EntityUtils.getContentCharSet(entity) == null ? CHARENCODE
						: EntityUtils.getContentCharSet(entity);

				return new String(EntityUtils.toByteArray(entity), charset);
			} else {
				return null;
			}
		}
	};

	

	/**
	 * ����Ĭ��http����ͷ
	 * 
	 * @param request
	 */
	protected void setDeaultRequestHeader(AbstractHttpMessage request) {
		if (USEGZIP) {
			request.addHeader("Accept-Encoding", "gzip");
		}
	}

	private Map<String, Object> convertToParamMap(List<NameValuePair> list) {
		Map<String, Object> paramMap = new HashMap<String, Object>();
		for (NameValuePair namevalue : list) {
			paramMap.put(namevalue.getName(), namevalue.getValue());
		}
		return paramMap;
	}
	private String genrateGetUrl(String url,List<NameValuePair> olist){
		
		if(olist != null && olist.size()>0){
			return url + "?" + URLEncodedUtils.format(olist, CHARENCODE);
		}else{
			return url;
		}
	}
	/**
	 * get请求
	 * 
	 * @param data
	 * @return
	 * @throws ParseException
	 * @throws IOException
	 */
	public String get(String url, List<NameValuePair> olist)
			throws ParseException, IOException {
		/**
		 * try { return standerHttp.connectURL(url,convertToParamMap(list),
		 * HttpURLConnectionUtil.CONNECTION_TYPE_GET); } catch (Exception e) {
		 * // TODO Auto-generated catch block e.printStackTrace(); } return "";
		 */
		String turl = genrateGetUrl(url,olist);
			CorePreferences.DEBUG("get:"+turl);
		HttpGet request = new HttpGet(turl);
		setDeaultRequestHeader(request);
		long starttime = System.currentTimeMillis();
		String s = client.execute(request, responseHandler);
		CorePreferences.DEBUG("result:" + s);
		CorePreferences.DEBUG("request-response took time :" + (System.currentTimeMillis() - starttime) + "ms");
		return s;
	}
	/**
	 * get请求，自定义超时
	 * @param url
	 * @param olist
	 * @param timeout
	 * @return
	 * @throws ParseException
	 * @throws IOException
	 */
	public String get(String url, List<NameValuePair> olist,int timeout)
			throws ParseException, IOException {
		String turl = genrateGetUrl(url,olist);
			CorePreferences.DEBUG("get:"+turl);
		HttpGet request = new HttpGet(turl);
		setDeaultRequestHeader(request);
		String s = createHttpClientWithHttpParams(createHttpParams(timeout)).execute(request, responseHandler);
		CorePreferences.DEBUG("result:" + s);
		return s;
	}
	/**
	 * get请求，返回 HttpEntity
	 * @param url
	 * @param olist
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public HttpEntity getWithHttpEntity(String url, List<NameValuePair> olist) throws ClientProtocolException, IOException {
		String turl = genrateGetUrl(url,olist);
		CorePreferences.DEBUG("get:"+turl);
		HttpGet request = new HttpGet(turl);
		setDeaultRequestHeader(request);
		HttpResponse response = client.execute(request);
		return  response.getEntity();
	}
	/**
	 * get请求，返回 HttpEntity,自定义超时时间
	 * @param url
	 * @param olist
	 * @param timeout
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public HttpEntity getWithHttpEntity(String url, List<NameValuePair> olist,int timeout) throws ClientProtocolException, IOException {
		String turl = genrateGetUrl(url,olist);
		CorePreferences.DEBUG("get:"+turl);
		HttpGet request = new HttpGet(turl);
		setDeaultRequestHeader(request);
		HttpResponse response = createHttpClientWithHttpParams(createHttpParams(timeout)).execute(request);
		return  response.getEntity();
	}
	/**
	 * get请求，返回 输入流
	 * @param url
	 * @param olist
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public InputStream getWithStream(String url, List<NameValuePair> olist) throws ClientProtocolException, IOException {
		return  getUngzippedStream(getWithHttpEntity(url, olist));
	}
	/**
	 * get请求，返回 输入流,自定义超时时间
	 * @param url
	 * @param olist
	 * @param timeout
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public InputStream getWithStream(String url, List<NameValuePair> olist,int timeout) throws ClientProtocolException, IOException {
		return  getUngzippedStream(getWithHttpEntity(url, olist,timeout));
	}
	
	/***
	 * post请求
	 * 
	 * @param list
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public String post(String url, HttpEntity entity)
			throws ClientProtocolException, IOException {
		CorePreferences.DEBUG("post:"+url);
		HttpPost request = new HttpPost(url);
		setDeaultRequestHeader(request);
		request.setEntity(entity);
		String s = client.execute(request, responseHandler);
		CorePreferences.DEBUG(s);
		return s;
	}
	/**
	 * post请求，设置最大超时限制
	 * @param url
	 * @param entity
	 * @param timeout
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public String post(String url, HttpEntity entity,int timeout)
			throws ClientProtocolException, IOException {
		CorePreferences.DEBUG("post:"+url);
		HttpPost request = new HttpPost(url);
		setDeaultRequestHeader(request);
		request.setEntity(entity);
		String s = createHttpClientWithHttpParams(createHttpParams(timeout)).execute(request, responseHandler);
		CorePreferences.DEBUG(s);
		return s;
	}
	public String post(String url, List<NameValuePair> olist)
			throws ClientProtocolException, IOException {
		return post(url,new UrlEncodedFormEntity(olist,CHARENCODE));
	}
	
	public InputStream postWithStream(String url, HttpEntity entity) throws ClientProtocolException, IOException{
		return  getUngzippedStream(postWithHttpEntity(url, entity));
	}
	public HttpEntity postWithHttpEntity(String url, HttpEntity entity) throws ClientProtocolException, IOException{
		CorePreferences.DEBUG(url);
		HttpPost request = new HttpPost(url);
		setDeaultRequestHeader(request);
		request.setEntity(entity);
		HttpResponse response =  client.execute(request);
		return response.getEntity();
	}
	public BasicNameValuePair generateGetParameter(String name, String value) {
		return new BasicNameValuePair(name, value);
	}
	 /**
     * Gets the input stream from a response entity. If the entity is gzipped then this will get a
     * stream over the uncompressed data.
     *
     * @param entity the entity whose content should be read
     * @return the input stream to read from
     * @throws IOException
     */
    public InputStream getUngzippedStream(HttpEntity entity) throws IOException {
        InputStream responseStream = entity.getContent();
        if (responseStream == null) {
            return responseStream;
        }
        Header header = entity.getContentEncoding();
        if (header == null) {
            return responseStream;
        }
        String contentEncoding = header.getValue();
        if (contentEncoding == null) {
            return responseStream;
        }
        if (contentEncoding.contains("gzip")) {
            responseStream = new GZIPInputStream(responseStream);
        }
        return responseStream;
    }
}
